package com.turtle.security.common.aspect;

import com.alibaba.fastjson.JSON;
import com.turtle.security.common.annotation.OperateLog;
import com.turtle.security.common.constant.HttpStatus;
import com.turtle.security.common.enums.RequestMethod;
import com.turtle.security.common.utils.CustomerStringUtils;
import com.turtle.security.common.utils.IpAddressUtils;
import com.turtle.security.common.utils.ServletUtils;
import com.turtle.security.system.entity.SysOperateLog;
import com.turtle.security.system.service.impl.SysOperateLogServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.HandlerMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Map;

/**
 * 操作日志处理
 *
 * @author turtle
 * date: 2020-06-05
 */
@Aspect
@Component
@Slf4j
public class OperateLogAspect {

    @Autowired
    private SysOperateLogServiceImpl operateLogService;

    /**
     * 编织切入点
     */
    @Pointcut("@annotation(com.turtle.security.common.annotation.OperateLog)")
    public void operateLogPointcut() {
    }

    /**
     * 处理返回时处理
     *
     * @param joinPoint  切入点
     * @param jsonResult 返回值
     */
    @AfterReturning(pointcut = "operateLogPointcut()", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, Object jsonResult) {
        handleOperateLog(joinPoint, jsonResult, null);
    }

    /**
     * 处理抛出异常使处理
     *
     * @param joinPoint 切入点
     * @param e         异常
     */
    @AfterThrowing(pointcut = "operateLogPointcut()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
        handleOperateLog(joinPoint, null, e);
    }

    /**
     * 处理操作日志
     *
     * @param joinPoint  切入点
     * @param jsonResult 返回值
     * @param e          异常
     */
    protected void handleOperateLog(final JoinPoint joinPoint, final Object jsonResult, final Exception e) {
        try {
            OperateLog operateLogAnnotation = getAnnotationOperateLog(joinPoint);
            if (CustomerStringUtils.isNull(operateLogAnnotation)) {
                return;
            }
            SysOperateLog sysOperateLog = new SysOperateLog();

            sysOperateLog.setStatus(HttpStatus.SUCCESS);
            // 操作ip
            String ip = IpAddressUtils.getIpAddress(ServletUtils.getRequest());
            sysOperateLog.setOperateIp(ip);
            // 操作地点
            sysOperateLog.setOperateLocation(IpAddressUtils.getAddressByIp(ip));
            // 操作方法
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            sysOperateLog.setMethod(className + "." + methodName);
            // 请求方式
            sysOperateLog.setRequestMethod(ServletUtils.getRequest().getMethod());
            // 请求地址
            sysOperateLog.setOperateUrl(ServletUtils.getRequest().getRequestURI());
            // 返回参数
            sysOperateLog.setJsonResult(JSON.toJSONString(jsonResult));

            // 获取注解上的参数
            getOperateLogParams(operateLogAnnotation, sysOperateLog);
            // 获取请求参数
            getRequestParams(joinPoint, sysOperateLog);

            if (CustomerStringUtils.isNotNull(e)) {
                // 抛出了异常
                sysOperateLog.setStatus(HttpStatus.ERROR);
                sysOperateLog.setErrorMsg(e.getMessage());
            }

            operateLogService.save(sysOperateLog);

        } catch (Exception exception) {
            log.error("操作日志处理异常信息：" + exception.getMessage());
        }


    }

    /**
     * 获取OperateLog注解
     *
     * @param joinPoint 切入点
     * @return OperateLog
     */
    private OperateLog getAnnotationOperateLog(JoinPoint joinPoint) {

        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        if (CustomerStringUtils.isNotNull(method)) {
            return method.getAnnotation(OperateLog.class);
        }
        return null;
    }

    /**
     * 获取OperateLog上的参数
     *
     * @param operateLog    日志注解
     * @param sysOperateLog 要保存的日志对象
     */
    private void getOperateLogParams(OperateLog operateLog, SysOperateLog sysOperateLog) {
        // 操作客户端
        sysOperateLog.setOperateClient(operateLog.operateClient().getClientName());
        // 操作模块
        sysOperateLog.setTitle(operateLog.module());
        // 操作类型
        sysOperateLog.setOperateType(operateLog.operateType().getTypeName());
        // 操作描述
        sysOperateLog.setOperateDescription(operateLog.description());
    }

    /**
     * 获取请求参数
     *
     * @param joinPoint     切入点
     * @param sysOperateLog 要保存的日志对象
     */
    private void getRequestParams(JoinPoint joinPoint, SysOperateLog sysOperateLog) {
        String params;
        String method = sysOperateLog.getRequestMethod();
        if (RequestMethod.POST.name().equals(method) || RequestMethod.PUT.name().equals(method)) {
            params = argsArrayToString(joinPoint.getArgs());
        } else {
            Map<?, ?> paramsMap = (Map<?, ?>) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
            params = paramsMap.toString();
        }
        sysOperateLog.setOperateParam(params);
    }

    /**
     * 请求参数拼装
     *
     * @param paramsArray 参数
     * @return 参数
     */
    private String argsArrayToString(Object[] paramsArray) {
        StringBuilder params = new StringBuilder();
        if (CustomerStringUtils.isNotNull(paramsArray) && paramsArray.length > 0) {
            for (Object o : paramsArray) {
                if (!isFilterObject(o)) {
                    Object jsonObj = JSON.toJSON(o);
                    params.append(jsonObj.toString()).append(" ");
                }
            }
        }
        return params.toString().trim();
    }

    /**
     * 判断是否需要过滤的对象。
     *
     * @param o 对象信息。
     * @return 如果是需要过滤的对象，则返回true；否则返回false。
     */
    public boolean isFilterObject(final Object o) {
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse;
    }


}
